package cheme.ProcessModel.Cmds;

import org.eclipse.gef.commands.Command;

import cheme.ProcessModel.IParent;
import cheme.ProcessModel.ProcessUnits.ITerminal;
import cheme.ProcessModel.ProcessUnits.ProcessUnit;
import cheme.ProcessModel.ProcessUnits.Streams.AbstractStream;


public class ReconnectStreamCmd extends Command
{
	IParent newParent = null;
	private IParent oldParent = null;
	
	private AbstractStream stream = null;

	private ProcessUnit newSource = null;
	private ProcessUnit newSink = null;

	private boolean removedOldSource = false;
	private ProcessUnit oldSource = null;

	private boolean removedOldSink = false;
	private ProcessUnit oldSink = null;

	public void execute()
	{
		if(newSource != null)
		{
			if(addToParentOnExecute())
			{
				newParent.add(newSource);
			}
			
			oldSource = stream.getSource();
			reconnectSource(newSource);
			
			if(oldSource instanceof ITerminal && 0 == oldSource.getOutStreams().size())
			{
				removedOldSource = true;
				oldParent = oldSource.getParent();
				oldParent.remove(oldSource);
			}
		}

		if(newSink != null)
		{
			if(addToParentOnExecute())
			{
				newParent.add(newSink);
			}
			
			oldSink = stream.getSink();
			reconnectTarget(newSink);
			
			if(oldSink instanceof ITerminal && 0 == oldSink.getInStreams().size())
			{
				removedOldSink = true;
				oldParent = oldSink.getParent();
				oldParent.remove(oldSink);
			}
		}
	}

	private void reconnectSource(ProcessUnit source)
	{
		stream.detachSource();
		stream.setSource(source);
		stream.attachSource();
	}

	private void reconnectTarget(ProcessUnit sink)
	{
		stream.detachSink();
		stream.setSink(sink);
		stream.attachSink();
	}
	
	private boolean addToParentOnExecute()
	{
		return null != newParent;
	}
	
	public void setParent(IParent parent)
	{
		this.newParent = parent;
	}

	public void setStream(AbstractStream stream)
	{
		this.stream = stream;
	}

	public void setNewSource(ProcessUnit newSource)
	{
		this.newSource = newSource;
	}

	public void setNewSink(ProcessUnit newSink)
	{
		this.newSink = newSink;
	}

	public void undo()
	{
		if(oldSource != null)
		{
			if(removedOldSource)
			{
				oldParent.add(oldSource);
			}
			
			if(addToParentOnExecute())
			{
				newParent.remove(newSource);
			}
			
			reconnectSource(oldSource);
		}

		if(oldSink != null)
		{
			if(removedOldSink)
			{
				oldParent.add(oldSink);
			}
			
			if(addToParentOnExecute())
			{
				newParent.remove(newSink);
			}

			reconnectTarget(oldSink);
		}

		oldSource = null;
		oldSink = null;
	}
}
